use std::assert;
use std::ffi::CString;
use std::ptr;
use std::sync::atomic::{AtomicUsize, Ordering};
macro_rules! alloc_test_assert {
    ($ expr : expr) => {
        num_assert.fetch_add(1, Ordering::SeqCst);
        if !$expr {
            panic!("Assertion failed: {}", stringify!($expr));
        }
    };
}
const BINOMIAL_HEAP_NULL: *mut std::ffi::c_void = std::ptr::null_mut();
const NUM_TEST_VALUES: usize = 10000;
const TEST_VALUE: usize = NUM_TEST_VALUES / 2;
type UnitTestFunction = fn() -> ();
static num_assert: AtomicUsize = AtomicUsize::new(0);
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq)]
enum BinomialHeapType {
    BINOMIAL_HEAP_TYPE_MIN,
    BINOMIAL_HEAP_TYPE_MAX,
}
type BinomialHeapValue = *mut std::ffi::c_void;
type BinomialHeapCompareFunc = fn(value1: BinomialHeapValue, value2: BinomialHeapValue) -> i32;
static mut test_array: [i32; NUM_TEST_VALUES] = [0; NUM_TEST_VALUES];
static tests: &[UnitTestFunction] = &[
    test_binomial_heap_new_free,
    test_binomial_heap_insert,
    test_min_heap,
    test_max_heap,
    test_insert_out_of_memory,
    test_pop_out_of_memory,
];
#[test]
fn test_binomial_heap_new_free() {
    for _ in 0..NUM_TEST_VALUES {
        let heap = binomial_heap_new(BINOMIAL_HEAP_TYPE_MIN, int_compare);
        binomial_heap_free(heap);
    }
    alloc_test_set_limit(0);
    assert!(binomial_heap_new(BINOMIAL_HEAP_TYPE_MIN, int_compare).is_null());
}
#[test]
fn test_binomial_heap_insert() {
    use std::ptr;
    use std::sync::atomic::{AtomicUsize, Ordering};
    static mut HEAP: *mut BinomialHeapType = ptr::null_mut();
    lazy_static! {
        static ref TEST_ARRAY: Vec<i32> = {
            let mut vec = Vec::with_capacity(NUM_TEST_VALUES);
            for i in 0..NUM_TEST_VALUES {
                vec.push(i as i32);
            }
            vec
        };
    }
    static OUT_OF_MEM: AtomicUsize = AtomicUsize::new(0);
    unsafe {
        HEAP = binomial_heap_new(BINOMIAL_HEAP_TYPE_MIN, int_compare);
        if HEAP.is_null() {
            panic!("Failed to create binomial heap");
        }
        for i in 0..NUM_TEST_VALUES {
            assert!(binomial_heap_insert(HEAP, &TEST_ARRAY[i]) != 0);
        }
        assert!(binomial_heap_num_entries(HEAP) == NUM_TEST_VALUES);
        alloc_test_set_limit(0);
        assert!(binomial_heap_insert(HEAP, &OUT_OF_MEM) == 0);
        binomial_heap_free(HEAP);
    }
}
#[test]
fn test_min_heap() {
    let mut heap = binomial_heap_new(HeapType::Min, int_compare);
    static mut TEST_ARRAY: [i32; NUM_TEST_VALUES] = [0; NUM_TEST_VALUES];
    for i in 0..NUM_TEST_VALUES {
        unsafe {
            TEST_ARRAY[i] = i as i32;
            assert!(binomial_heap_insert(&mut heap, Box::new(TEST_ARRAY[i])) != 0);
        }
    }
    let mut i = -1;
    while binomial_heap_num_entries(&heap) > 0 {
        let val = binomial_heap_pop(&mut heap).unwrap();
        assert!(*val == i + 1);
        i = *val;
    }
    let val = binomial_heap_pop(&mut heap);
    assert!(val.is_none());
    binomial_heap_free(heap);
}
#[test]
fn test_max_heap() {
    let heap = binomial_heap_new(BINOMIAL_HEAP_TYPE_MAX, int_compare);
    let mut test_array = vec![0; NUM_TEST_VALUES];
    for i in 0..NUM_TEST_VALUES {
        test_array[i] = i;
        assert!(binomial_heap_insert(heap, &test_array[i]).is_some());
    }
    let mut i = NUM_TEST_VALUES;
    while binomial_heap_num_entries(heap) > 0 {
        let val = binomial_heap_pop(heap).unwrap();
        assert_eq!(*val, i - 1);
        i = *val;
    }
    let val: Option<&i32> = binomial_heap_pop(heap);
    assert!(val.is_none());
    binomial_heap_free(heap);
}
fn generate_heap() -> *mut BinomialHeap {
    unsafe {
        let heap = binomial_heap_new(BINOMIAL_HEAP_TYPE_MIN, int_compare);
        for i in 0..NUM_TEST_VALUES {
            test_array[i] = i as i32;
            if i != TEST_VALUE as usize {
                assert!(binomial_heap_insert(heap, &test_array[i]) != 0);
            }
        }
        heap
    }
}
fn verify_heap(heap: *mut BinomialHeap) {
    let mut num_vals = binomial_heap_num_entries(heap);
    assert!(num_vals == NUM_TEST_VALUES - 1);
    for i in 0..NUM_TEST_VALUES {
        if i == TEST_VALUE {
            continue;
        }
        let val = binomial_heap_pop(heap);
        assert!(*val == i as i32);
        num_vals -= 1;
        assert!(binomial_heap_num_entries(heap) == num_vals);
    }
}
#[test]
fn test_insert_out_of_memory() {
    let mut heap: *mut BinomialHeapType;
    for i in 0..6 {
        heap = generate_heap();
        alloc_test_set_limit(i);
        TEST_ARRAY[TEST_VALUE] = TEST_VALUE as i32;
        assert!(binomial_heap_insert(heap, &TEST_ARRAY[TEST_VALUE]) == 0);
        alloc_test_set_limit(-1);
        verify_heap(heap);
        binomial_heap_free(heap);
    }
}
#[test]
fn test_pop_out_of_memory() {
    for i in 0..6 {
        let heap: *mut binomialheap::BinomialHeap;
        unsafe {
            heap = generate_heap();
            alloc_test_set_limit(i);
            assert!(binomial_heap_pop(heap).is_null());
            alloc_test_set_limit(-1);
            binomial_heap_free(heap);
        }
    }
}
fn main() {
    run_tests(tests);
    println!("num_assert: {}", num_assert);
}
